package gant.ext

import org.apache.tools.ant.Project
import org.codehaus.gant.GantBuilder
import org.codehaus.gant.GantState
import org.slf4j.ILoggerFactory
import org.slf4j.Logger
import org.slf4j.helpers.MarkerIgnoringBase
import org.slf4j.helpers.MessageFormatter

/**
 * Creates a sl4j ant logger that delegates logging to an ant instance
 */
class AntLoggingFactory implements ILoggerFactory {

    static final EMPTY = ""
    static final GANT = "gant"
    private static final loggingCache = [:]


    @Override
    Logger getLogger(String name) {
        if(!name || name == EMPTY || name == Logger.ROOT_LOGGER_NAME) {
            return getFromCache(GANT)
        }
        getFromCache(name)
    }

    private Logger getFromCache(String name) {
        if(!loggingCache.containsKey(name)) {
            loggingCache.put(name, new AntLoggerFacade(name: name))
        }

        loggingCache[name]
    }
}

/**
 * Actually does the logging
 */
class AntLoggerFacade extends MarkerIgnoringBase {

    private _gantToSlf4j = [:]
    GantBuilder builder
    String name
    protected static final SPACE_TO_LINE_UP_TO_ECHO = "     "

    /**
     * A simple log formatter.  The default logger attempts to line up the logger in the same position of the echo task
     */
    Closure logFormatter = {
        //the space lines up logging with the echo task
        def formattedMessage = new StringBuilder()
        formattedMessage.append(SPACE_TO_LINE_UP_TO_ECHO)
        formattedMessage.append("[")
        formattedMessage.append(level)
        formattedMessage.append("] ")

        def shouldAddName = name && "gant" != name
        if(shouldAddName) {
            formattedMessage.append(name)
            formattedMessage.append(" - ")
        }

        formattedMessage.append(message)
    }

    String formatLog(String message, String level) {
        def now = new Date()
        def delegate = [date: now, message: message, level: level]
        logFormatter.delegate = delegate
        logFormatter.call()
    }

    def getGantToSlf4j() {
        if (_gantToSlf4j) {
            return _gantToSlf4j
        }

        _gantToSlf4j.put("${GantState.DEBUG}" as String, "trace")
        _gantToSlf4j.put("${GantState.VERBOSE}" as String, "debug")
        _gantToSlf4j.put("${GantState.NORMAL}" as String, "info")
        _gantToSlf4j.put("${GantState.WARNINGS_AND_ERRORS}" as String, "warn")
        _gantToSlf4j.put("${GantState.ERRORS_ONLY}" as String, "error")

        // silent doesn't match anything, just put it at error level
        _gantToSlf4j.put("${GantState.SILENT}" as String, "error")

        return _gantToSlf4j
    }

    GantBuilder getBuilder() {
        if (builder) {
            return builder
        }
        builder = AntBindingFactory.getInstance().binding.ant
    }

    Project getProject() {
        getBuilder().project
    }

    @Override
    boolean isTraceEnabled() {
        isEnabled(GantState.DEBUG)
    }

    @Override
    void trace(String message) {
        doLogging(message, GantState.DEBUG)
    }

    @Override
    void trace(String format, Object arg) {
        trace(format, [arg] as Object[])
    }

    @Override
    void trace(String format, Object arg1, Object arg2) {
        trace(format, [arg1, arg2] as Object[])
    }

    @Override
    void trace(String format, Object[] argArray) {
        logIfEnabled(GantState.DEBUG, format, argArray)
    }

    @Override
    void trace(String msg, Throwable t) {
       doLogging(msg, GantState.DEBUG, t)
    }

    @Override
    boolean isDebugEnabled() {
        isEnabled(GantState.VERBOSE)
    }

    boolean isEnabled(Integer integer) {
        integer <= GantState.verbosity ||
            (GantState.SILENT == GantState.verbosity && integer == GantState.ERRORS_ONLY)
    }

    void logIfEnabled(Integer level, String message, Object[] args) {
        if (isEnabled(level)) {
            def formattedMessage = MessageFormatter.arrayFormat(message, args).message
            def slf4jLevel = gantToSlf4j[String.valueOf(level)]
            "${slf4jLevel}"(formattedMessage)
        }
    }

    @Override
    void debug(String message) {
        doLogging(message, GantState.VERBOSE)
    }

    private void doLogging(String message, int state, Throwable t = null) {
        def slf4jLevel = getGantToSlf4j()[String.valueOf(state)]
        if (t) {
            project.log(formatLog(message, slf4jLevel), t, state)
        } else {
            project.log(formatLog(message, slf4jLevel), state)
        }
    }

    @Override
    void debug(String format, Object arg) {
        debug(format, [arg] as Object[])
    }

    @Override
    void debug(String format, Object arg1, Object arg2) {
        debug(format, [arg1, arg2] as Object[])
    }

    @Override
    void debug(String format, Object[] argArray) {
        logIfEnabled(GantState.VERBOSE, format, argArray)
    }

    @Override
    void debug(String msg, Throwable t) {
        doLogging(msg, GantState.VERBOSE, t)
    }

    @Override
    boolean isInfoEnabled() {
        isEnabled(GantState.NORMAL)
    }

    @Override
    void info(String message) {
        doLogging(message, GantState.NORMAL)
    }

    @Override
    void info(String format, Object arg) {
        info(format, [arg] as Object[])
    }

    @Override
    void info(String format, Object arg1, Object arg2) {
        info(format, [arg1, arg2] as Object[])
    }

    @Override
    void info(String format, Object[] argArray) {
        logIfEnabled(GantState.NORMAL, format, argArray)
    }

    @Override
    void info(String msg, Throwable t) {
        doLogging(msg, GantState.NORMAL, t)
    }

    @Override
    boolean isWarnEnabled() {
        isEnabled(GantState.WARNINGS_AND_ERRORS)
    }

    @Override
    void warn(String message) {
        doLogging(message, GantState.WARNINGS_AND_ERRORS)
    }

    @Override
    void warn(String format, Object arg) {
        warn(format, [arg] as Object[])
    }

    @Override
    void warn(String format, Object[] argArray) {
        logIfEnabled(GantState.WARNINGS_AND_ERRORS, format, argArray)
    }

    @Override
    void warn(String format, Object arg1, Object arg2) {
        warn(format, [arg1,arg2] as Object[])
    }

    @Override
    void warn(String msg, Throwable t) {
        doLogging(msg, GantState.WARNINGS_AND_ERRORS, t)
    }

    @Override
    boolean isErrorEnabled() {
        isEnabled(GantState.ERRORS_ONLY)
    }

    @Override
    void error(String message) {
        doLogging(message, GantState.ERRORS_ONLY)
    }

    @Override
    void error(String format, Object arg) {
        error(format, [arg] as Object[])
    }

    @Override
    void error(String format, Object arg1, Object arg2) {
        error(format, [arg1, arg2] as Object[])
    }

    @Override
    void error(String format, Object[] argArray) {
        logIfEnabled(GantState.ERRORS_ONLY, format, argArray)
    }

    @Override
    void error(String msg, Throwable t) {
        doLogging(msg, GantState.ERRORS_ONLY, t)
    }
}
